/* ================================================== */
/* parser pour l'analyse syntaxique d'un fichier mop  */
/* author Mathieu Barbin                              */
/* version samedi 3 janvier 2009, 11:27:58 (UTC+0100) */
/* ================================================== */

%{
  open Maptypes

  let case_ignored () =
    prerr_endline (Printf.sprintf "[!] mop-file \"%s\" line %d : syntax error (case ignored)" !mop_file !mop_line) 

%}

%token TAG_INCLUDE
%token DEFINE
%token MAP
%token WITH
%token FLECHE
%token BARRE

%token <int> ARG
%token <string> PARTIAL
%token <string> IDENT
%token <string> NET_IDENT

%token <string> STRING
%token EOF

%type <Maptypes.bopmop_ast> mop_ast
%start mop_ast

%%

mop_ast : 
  | include_list define_list module_list EOF
      {
	{
	  includes = $1;
	  defines = $2;
	  modules = $3
	}
      }
;

include_list:
  |                            { [] }
  | include_file include_list  { $1::$2 }
;

arg_list :
  |              { [] }
  | ARG arg_list { $1::$2 }
;

define_list:
  |   { [] }
  | define_map define_list  { $1::$2 }
;

module_list:
  |   { [] }
  | module_map module_list  { $1::$2 }
;

include_file:
  | TAG_INCLUDE STRING 
      {
        (!mop_line, $2)
      }
;

pattern :
  | IDENT 
      { 
	if $1 = "_" then Some Any else 
	  begin
	    case_ignored ();
	    None
	  end 
      }
  | PARTIAL 
      {
	let len = String.length $1 in
	let p = Array.make len None in
	let rec aux i =
	  if i = len then Some (Specified p)
	  else
	    match $1.[i] with
	    | '1' -> p.(i) <- Some true; aux (succ i)
	    | '0' -> p.(i) <- Some false; aux (succ i)
	    | '*' -> p.(i) <- None; aux (succ i)
	    | _ -> case_ignored () ; None
	in aux 0
      }
;

fleche : 
  | FLECHE { !mop_line }
;

matching_item :
  | BARRE pattern fleche STRING arg_list
      {
	match $2 with
	| None -> None
	| Some p -> Some ($3, (p, $4, $5))
      }
;

matching :
  | matching_item { match $1 with None -> [] | Some u -> [u] }
  | matching_item matching { match $1 with None -> $2 | Some u -> u::$2 }
;

define_map :
  | DEFINE MAP IDENT matching { $3, $4 }
;

variable_map :
  | MAP NET_IDENT WITH matching { $2, Anonymous $4 }
  | MAP IDENT WITH matching     { $2, Anonymous $4 }
  | MAP IDENT WITH IDENT        { $2, UseDefine $4 }
  | MAP NET_IDENT WITH IDENT    { $2, UseDefine $4 }
;

module_map:
  | IDENT variable_map_list { $1, $2 }
;

variable_map_list :
  |   { [] }
  | variable_map variable_map_list{ $1::$2 }
;
